package com.yc.spring.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * 这是一个切面类  = 切点（方法 + 注解属性） + 增强（方法）
 */
@Component
@Aspect
public class MyAspect {

    //前置增强
    /*      "*"：表示任何返回类型的方法。

            "com.yc.spring.bbs.dao.UserDaoImpl": 是类的全路径名称，表明要应用切点的类是UserDaoImpl。

            ".*": 表示类中的所有方法都被包含在内。

            "(..)": 表示方法的参数可以是任意数量和类型。

             所以这个表达式的含义就是：当执行UserDaoImpl类中的任何*/
    @Before("execution(* com.yc.spring.bbs.dao.*.*(..))")
    public void before() {
        System.out.println("前置拦截");
    }

    //spring 会以接口注入方式，将连接点对象注入进来
    @Before("execution(* com.yc.spring.bbs.dao.UserDaoImpl.*(..))")
    public void before1(JoinPoint jp) {
        System.out.println("==============前置拦截 begin==============");

        //参数
        System.out.println("Arrays.toString(jp.getArgs()) = " + Arrays.toString(jp.getArgs()));

        //方法签名
        System.out.println("jp.getSignature() = " + jp.getSignature());

        // 目标对象
        System.out.println("jp.getTarget() = " + jp.getTarget());
        System.out.println("==============前置拦截 end==============");
    }


    @After("myPointcut()")
    public void after(JoinPoint jp) {
        System.out.println("-----------后置拦截" + jp.getSignature() + "---------------");
    }


    //切点方法
    @Pointcut("execution(* com.yc.spring.bbs.dao.*.*(..))")
    public void myPointcut() {

    }


    //返回拦截  =>  只有业务方法成功执行才拦截，并且可以注入业务方法的返回值
    @AfterReturning(value = "myPointcut()", returning = "ret")  //引用切点
    public void afterReturning(JoinPoint jp, Object ret) {
        System.out.println("-----------返回拦截" + jp.getSignature() + "---------------");
        System.out.println("-----------返回拦截" + ret + "---------------");
    }

    //异常链接  =>  只有业务方法执行失败才拦截，并且可以注入该异常
    @AfterThrowing(value = "myPointcut()", throwing = "e")  //引用切点
    public void afterThrowing(JoinPoint jp, Throwable e) {
        System.out.println("-----------异常拦截" + jp.getSignature() + "---------------");
        System.out.println("-----------异常拦截" + e + "---------------");
    }


    @Before("execution(* com.yc..biz.UserBiz.remove(..))")
    public void before2(JoinPoint jp){
        System.out.println("======================================= 前置拦截 =======================================");
    }


    /**
     * 环绕增强
     * 返回值：是业务方法的返回值
     * 连接点对象负责执行业务方法
     *
     * @param pjp
     * @return
     */
    @Around("execution(* com.yc.spring.bbs.dao.UserDaoImpl.count(..))")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        try {
            System.out.println("环绕增强 => 前置增强");
            long time = System.currentTimeMillis();
            Object ret = pjp.proceed();   //连接点对象执行业务方法，手写返回值
            time = System.currentTimeMillis() - time;
            System.out.println(pjp + "业务方法执行了" + time + "毫秒");
            System.out.println("环绕增强 => 返回增强");
            return ret;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("环绕增强 => 异常增强");
            throw throwable;
        } finally {
            System.out.println("环绕增强 => 后置增强");
        }
    }


    /**
     *
     * try{
     *                    前置增强
     *    业务方法
     *                    返回增强
     * } catch (){
     *                    异常增强
     * } finally (){
     *     不管怎么样都会执行
     *                   后置增强
     * }
     *
     *  性能监控
     */
}
